home *** CD-ROM | disk | FTP | other *** search
/ PC Media 20 / PC MEDIA CD20.iso / share / prog / cursoasm / cap5.msg < prev    next >
Text File  |  1993-06-26  |  15KB  |  274 lines

  1.                       INTRODUCCION AL ASM: LA PILA
  2.                       ============================
  3.  
  4.    Como comentábamos al final del cuarto capítulo, en este quinto capítulo
  5. vamos a estudiar uno de los elementos de mayor utilidad del ASM: la pila.
  6. Al estudiarla veremos dos nuevos registros, el SS y el SP, y dos nuevas
  7. instrucciones, PUSH y POP.
  8.  
  9.    Primero explicaré lo que se entiende por una pila en general y después vere-
  10. mos la pila del 8086 en concreto. Por eso, si alguien sabe lo que es una pila,
  11. puede saltarse algunas líneas.
  12.  
  13.    Un ordenador siempre guarda los datos en la memoria, exceptuando los regis-
  14. tros de la CPU. Estos datos pueden estar dispuestos de diferentes maneras, y se
  15. puede acceder a ellos de diversas formas. Una particular organización y modo de
  16. acceso a los datos se suele denominar 'estructura de datos', de las que existen
  17. muchas y muy diferentes, cada una adecuada para un propósito. Son estructuras
  18. de datos los arrays, las listas encadenadas, los árboles binarios, etc...
  19.  
  20.    Las estructuras que nos interesan ahora aquí son las estructuras lineales,
  21. en que cada elemento (cada dato) va después del anterior. Es decir, cada ele-
  22. mento tiene un predecesor y un sucesor, excepto el primero y el último. Son
  23. estructuras de este tipo los arrays y las listas encadenadas, mientras que no
  24. lo son los árboles binarios (no os preocupéis los que no sepáis que son estas
  25. cosillas). Las estructuras lineales se pueden clasificar principalmente en dos
  26. grupos: las llamadas FIFO (First In First Out, el primero en entrar es el pri-
  27. mero en salir) y las llamadas LIFO (Last In First Out, el último en entrar es
  28. el primero en salir). Aquí van dos ejemplos de cada una para que quede claro:
  29.  
  30.    Una estructura FIFO es similar a la cola del autobús: los elementos se orde-
  31. nan según llegan (los pasajeros se ponen a la cola cada uno detrás del anterior
  32. en llegar) y se sacan empezando por el primero que llegó y acabando por el úl-
  33. timo (el primero en llegar a la parada es el primero en montar). Esta estructu-
  34. ra se suele denominar 'cola' o 'queue' ('cola' en inglés).
  35.  
  36.    En cambio, una estructura LIFO es similar al montón de papeles que suelen
  37. estar pinchados todos juntos en una tintorería: los elementos se van ordenando
  38. según llegan (cada papel se pincha sobre el anterior), pero al sacarse del
  39. montón se sacan empezando por el último que llegó (se empiezan a sacar por el
  40. último que se pinchó, justo en orden inverso al que se pincharon). Esta estruc-
  41. tura se suele denominar 'pila' o 'stack' ('pila' en inglés).
  42.  
  43.    Estas estructuras se pueden implementar de muchas maneras diferentes por un
  44. programa (todavía no estamos viendo la pila del 8086 en concreto, ésta viene ya
  45. implementada por el hard del uP). Una manera sencilla de hacerlo es un array
  46. del tipo de dato que queremos cada elemento: si queremos una cola de enteros
  47. será un array de enteros, si queremos una pila de números reales será un array
  48. de números en coma flotante, etc... Necesitaremos también una variable entera
  49. que llevará la cuenta de los elementos insertados hasta el momento, que llama-
  50. remos 'cuenta'. Veamos cómo se implementaría una pila con este esquema:
  51. inicialmente, la variable 'cuenta' valdría -1 (suponemos que los índices de los
  52. arrays van como en C, comenzando por el cero). Para insertar un elemento, se
  53. incrementaría 'cuenta' y se guardaría el elemento en la posición número 'cuen-
  54. ta' del array. De esta manera, siempre tendríamos accesible el último valor
  55. empujado en 'pila[cuenta]'. Una jugada inteligente sería meter en este punto
  56. una comprobación de que 'cuenta' no ha sobrepasado el límite superior de la
  57. pila. En caso de que así fuera, emitiríamos un mensaje de error. Este error se
  58. suele denominar 'stack overflow' o 'desbordamiento de la pila', y aunque en la
  59. pila del 8086 no se comprueba internamente, los compiladores de lenguajes de
  60. alto nivel suelen incluir código para comprobarlo. A esta operación de intro-
  61. ducir un valor en la pila se le suele llamar 'empujar' un valor en la pila (en
  62. inglés 'push').
  63.  
  64.    La otra operación que nos queda ver es la operación contraria, la de sacar
  65. un valor de la pila. Ya que queremos acceder a la estructura en la forma LIFO,
  66. cada vez que se extrae un valor debe ser del extremo superior de la pila. Por
  67. ejemplo, si 'cuenta' vale 4 y queremos extraer un valor, éste deberá tomarse de
  68. la posición 4 del array, que será el último elemento empujado. Además, hay que
  69. decrementar la variable 'cuenta' para apuntar al nuevo 'tope' de la pila. Es
  70. importante tener en cuenta que no hace falta sobreescribir la posición 4 del
  71. array, esto ocurrirá la próxima vez que se empuje un valor, sino que basta con
  72. decrementar la variable 'cuenta'. A esta otra operación se le llama 'pop' en
  73. inglés, en español se suele decir simplemente 'sacar' o 'extraer' un valor de
  74. la pila.
  75.  
  76.    La pila del 8086 viene implementada por el hard del uP, pero los datos resi-
  77. den en memoria. El uP tiene dos registros para manejar la pila: el SS ('Stack
  78. Segment', 'segmento de pila'), que es el cuarto registro de segmento que junto
  79. con CS, DS y ES se utilizan para formar direcciones de 20 bits, y el SP ('Stack
  80. Pointer', 'puntero de pila'), que es un registro de 16 bits que cumple la
  81. función de nuestra variable 'cuenta' y que se une al SP para formar la direc-
  82. ción de memoria completa. Cada programa suele destinar una zona de la memoria
  83. para la pila, y es esta zona la que alberga los valores introducidos en ésta.
  84. Todos los elementos de la pila son valores enteros de 16 bits, por lo que se
  85. utiliza una palabra de la memoria para cada valor.
  86.  
  87.    Un aspecto que en un principio puede llamar la atención es que la pila del
  88. 8086, al igual que la de todos los micros que conozco, crece hacia abajo en
  89. lugar de hacia arriba. El funcionamiento de ésta es, por lo demás, idéntico al
  90. de nuestra pila imaginaria: al principio 'cuenta' apuntaría al último elemento
  91. del array más uno, al empujar un valor se se decrementaría cuenta y metería en
  92. 'pila[cuenta]' , y al sacarlo se incrementaría cuenta y se devolvería
  93. 'pila[cuenta]'. Más adelante se verán las ventajas de que la pila crezca hacia
  94. abajo.
  95.  
  96.    Los registros SS y SP forman la dirección absoluta donde reside el último
  97. valor empujado. Al empujar un valor, se resta dos a SP (porque cada elemento
  98. son dos bytes, es decir, dos posiciones de memoria) y el nuevo valor se guarda
  99. en la dir SS:SP (y el byte de mayor peso en SS:SP+1). Al sacar un valor, se
  100. toma de las posiciones SS:SP y SS:SP+1 y se suma dos a SP.
  101.  
  102.    Las instrucciones PUSH y POP nos permiten empujar un valor de 16 bits y
  103. recoger el último valor empujado. El uP se encarga de actualizar SP.
  104.  
  105.    Vamos a ver un ejemplo un poco 'incivilizado' de cómo pondríamos a punto la
  106. pila en una dirección determinada. Digo 'incivilizado' porque lo hacemos en una
  107. dirección arbitraria, donde en un PC podría haber un driver o cualquier cosa.
  108. De esto normalmente se encarga la rutina del DOS que carga un programa y lo
  109. ejecuta, por lo que sólo es un ejemplo para comprender el funcionamiento.
  110.  
  111.    Como sagazmente habréis deducido (y por si acaso no, os lo cuento), ya que
  112. la gestión de la pila se hace modificando sólo el SP al hacer PUSH y POP, la
  113. pila no puede exceder de 64K. Supongamos que queremos poner una pila de 64K en
  114. el segmento que comienza en la dirección absoluta 80000h (8000h:0). La última
  115. palabra de este segmento está en 8FFFEh (el byte de menor peso en ésta y el de
  116. mayor en la 8FFFFh). Por tanto, habría que cargar los registros como sigue:
  117.  
  118.         MOV    AX,8000h
  119.         MOV    SS,AX
  120.         MOV    SP,0FFFEh
  121.  
  122.    Más adelante veremos que esto no se puede hacer así, sino que hay que tener
  123. en cuenta las interrupciones, etc.. Por lo que no lo probéis. Pero para hacerse
  124. una idea, está bien. En realidad, no es necesario reservar 64K para la pila,
  125. por lo que SP no tiene por qué inicializarse a 0FFFEh. Por ejemplo, si queremos
  126. reservar 32K para la pila inicializaremos SP a 7FFEh.
  127.  
  128.    El funcionamiento exacto de la pila es el siguiente: cuando se ejecuta una
  129. instrucción PUSH se decrementa SP en dos unidades y se guarda en la dirección
  130. SS:SP el valor a empujar. De esta forma, SS:SP siempre apunta al último valor
  131. empujado. Cuando se ejecuta un POP, se recoge el valor de la dirección SS:SP y
  132. se incrementa SP en dos unidades.
  133.  
  134.    Ambas instrucciones llevan un solo operando, que especifica de donde se toma
  135. el valor (en el caso de PUSH) o donde se almacena (en el caso de POP). Los
  136. operandos pueden ser uno cualquiera de los siguientes:
  137.  
  138.    - Un registro de 16 bits de los siguientes: AX, BX, CX, DX, SI, DI, BP o SP.
  139.    - Un registro de segmento (no se permite POPear CS, porque implicaría un
  140.      salto del programa a otra dirección, y para esto ya hay otras instruccio-
  141.      nes).
  142.    - Una referencia a memoria (con cualquiera de los modos de direccionamiento
  143.      que vimos con la instrucción MOV).
  144.    - Un valor inmediato (por ejemplo, PUSH 55AAh) (sólo para PUSH y en 386 o
  145.      superiores).
  146.  
  147.    Veamos un pequeño listado en ASM y los contenidos de la pila y los registros
  148. con cada instrucción. El listado es el siguiente:
  149.  
  150.         MOV  AX,8000h
  151.         MOV  SS,AX
  152.         MOV  SP,0FFFEh        ; inicializa la pila
  153.         MOV  AX,55AAh
  154.         PUSH AX            ; empuja el valor 55AAh
  155.         MOV  BX,AX
  156.         ADD  BX,1111h        ; suma a BX el valor 1111h
  157.         PUSH BX
  158.         POP  AX
  159.         POP  BX
  160.  
  161.    Estudiemos lo que ocurre a cada paso:
  162.  
  163. *        MOV  AX,8000h
  164. *        MOV  SS,AX
  165. *        MOV  SP,0FFFEh        ; inicializa la pila
  166.  
  167.    Estas instrucciones inicializan SS y SP para situar la pila al final del
  168. segmento que comienza en la dirección absoluta 80000h. En este caso, no nos
  169. interesan los contenidos anteriores de esa zona de memoria. Representaremos el
  170. contenido de la pila como sigue:
  171.  
  172.      Dirección de memoria                      Valor         Registros del uP
  173.    --------------------------------------    ------------   ------------------
  174.    Segmento   Offset   Dirección absoluta       Valor           SS = 8000h
  175.    --------   ------   ------------------       -----           SP = 0FFFEh
  176.     8000h     0FFFFh        8FFFFh .............. ??
  177.     8000h     0FFFEh        8FFFEh .............. ??
  178.     8000h     0FFFDh        8FFFDh .............. ??
  179.     8000h     0FFFCh        8FFFCh .............. ??
  180.     8000h     0FFFBh        8FFFBh .............. ??
  181.     8000h     0FFFAh        8FFFAh .............. ??
  182.  
  183. *        MOV  AX,55AAh
  184.  
  185.  Después de esta instrucción, el registro AX contendrá 55AAh.
  186.  
  187. *        PUSH AX            ; empuja el valor 55AAh
  188.  
  189.    Nada más ejecutarla, así quedarán la pila y los registros del micro:
  190.  
  191.      Dirección de memoria                      Valor         Registros del uP
  192.    --------------------------------------    ------------   ------------------
  193.    Segmento   Offset   Dirección absoluta       Valor           SS = 8000h
  194.    --------   ------   ------------------       -----           SP = 0FFFCh
  195.     8000h     0FFFFh        8FFFFh .............. ??            AX = 55AAh
  196.     8000h     0FFFEh        8FFFEh .............. ??
  197.     8000h     0FFFDh        8FFFDh .............. 55h
  198.     8000h     0FFFCh        8FFFCh .............. AAh
  199.     8000h     0FFFBh        8FFFBh .............. ??
  200.     8000h     0FFFAh        8FFFAh .............. ??
  201.  
  202. *        MOV  BX,AX
  203. *        ADD  BX,1111h        ; suma a BX el valor 1111h
  204. *        PUSH BX
  205.  
  206.    Ya que 55AAh + 1111h = 66BBh, así quedarán las cosas:
  207.  
  208.      Dirección de memoria                      Valor         Registros del uP
  209.    --------------------------------------    ------------   ------------------
  210.    Segmento   Offset   Dirección absoluta       Valor           SS = 8000h
  211.    --------   ------   ------------------       -----           SP = 0FFFAh
  212.     8000h     0FFFFh        8FFFFh .............. ??            AX = 55AAh
  213.     8000h     0FFFEh        8FFFEh .............. ??            BX = 66BBh
  214.     8000h     0FFFDh        8FFFDh .............. 55h
  215.     8000h     0FFFCh        8FFFCh .............. AAh
  216.     8000h     0FFFBh        8FFFBh .............. 66h
  217.     8000h     0FFFAh        8FFFAh .............. BBh
  218.  
  219. *        POP  AX
  220.  
  221.    Se tomará el valor de SS:SP, se guardará en AX, y se sumará dos a SP:
  222.  
  223.      Dirección de memoria                      Valor         Registros del uP
  224.    --------------------------------------    ------------   ------------------
  225.    Segmento   Offset   Dirección absoluta       Valor           SS = 8000h
  226.    --------   ------   ------------------       -----           SP = 0FFFCh
  227.     8000h     0FFFFh        8FFFFh .............. ??            AX = 66BBh
  228.     8000h     0FFFEh        8FFFEh .............. ??            BX = 66BBh
  229.     8000h     0FFFDh        8FFFDh .............. 55h
  230.     8000h     0FFFCh        8FFFCh .............. AAh
  231.     8000h     0FFFBh        8FFFBh .............. 66h
  232.     8000h     0FFFAh        8FFFAh .............. BBh
  233.  
  234. *        POP  BX
  235.  
  236.    Análogo a la instrucción anterior, pero con BX:
  237.  
  238.      Dirección de memoria                      Valor         Registros del uP
  239.    --------------------------------------    ------------   ------------------
  240.    Segmento   Offset   Dirección absoluta       Valor           SS = 8000h
  241.    --------   ------   ------------------       -----           SP = 0FFFEh
  242.     8000h     0FFFFh        8FFFFh .............. ??            AX = 66BBh
  243.     8000h     0FFFEh        8FFFEh .............. ??            BX = 55AAh
  244.     8000h     0FFFDh        8FFFDh .............. 55h
  245.     8000h     0FFFCh        8FFFCh .............. AAh
  246.     8000h     0FFFBh        8FFFBh .............. 66h
  247.     8000h     0FFFAh        8FFFAh .............. BBh
  248.  
  249.    Os habréis dado cuenta de que podríamos haber inicializado SP a 0 para apro-
  250. vechar también los dos últimos bytes del segmento. En realidad, esto es algo
  251. de lo que no es necesario preocuparse prácticamente nunca, pues el DOS se en-
  252. carga de inicializar la pila generalmente.
  253.  
  254.    Uno de los usos más habituales de la pila es el de preservar el contenido de
  255. uo o varios registros que van a ser utilizados en un fragmento de código. Suele
  256. ser algo así:
  257.  
  258.         PUSH AX        ; Guardamos AX, BX y CX
  259.         PUSH BX
  260.         PUSH CX
  261.         ; [...] Código que modifica AX, BX y CX
  262.         POP  CX        ; Recuperamos AX, BX y CX
  263.         POP  BX
  264.         POP  CX
  265.  
  266.    Ahora ya estamos preparados para introducir un importante aspecto de la
  267. programación en ASM: las interrupciones. Conociendo las interrupciones, ya ten-
  268. dremos el bagaje necesario para acometer la escritura de programas usando
  269. ensambladores comerciales como el TASM o el MASM, comenzando con el ya tradi-
  270. cional 'Hello, world'.
  271.  
  272.    Salut :-)
  273.  
  274.    Jon